package com.dsl.dslsuzhou.service;

import com.dsl.dslsuzhou.model.dto.CameraCountRequest;
import com.dsl.dslsuzhou.model.dto.CameraCountResponse;
import com.dsl.dslsuzhou.model.dto.DeviceQueryRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.retry.Retry;

import javax.annotation.PostConstruct;
import java.lang.management.ManagementFactory;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.stream.Collectors;

/**
 * 摄像头统计优化服务
 * 集成缓存、并发控制、动态批次调整等性能优化
 */
@Service
@Slf4j
public class CameraCountOptimizedService {
    
    @Autowired
    private CameraService cameraService;
    
    @Autowired
    private DeviceService deviceService;
    
    // 批次配置
    @Value("${camera.batch.base-size:150}")
    private int baseBatchSize;
    
    @Value("${camera.batch.max-size:300}")
    private int maxBatchSize;
    
    @Value("${camera.batch.min-size:50}")
    private int minBatchSize;
    
    @Value("${camera.batch.max-concurrent:5}")
    private int maxConcurrentBatches;
    
    @Value("${camera.batch.timeout-seconds:10}")
    private int timeoutSeconds;
    
    // 并发控制
    private Semaphore batchSemaphore;
    
    @PostConstruct
    public void init() {
        this.batchSemaphore = new Semaphore(maxConcurrentBatches);
        log.info("摄像头统计服务初始化完成: baseBatchSize={}, maxConcurrent={}, timeout={}s", 
                baseBatchSize, maxConcurrentBatches, timeoutSeconds);
    }
    
    /**
     * 根据设备条件统计摄像头数量（带性能优化）
     */
    public Mono<CameraCountResponse> countCamerasByConditions(String category, String area, 
                                                            String building, String floor, 
                                                            Integer status, String token) {
        long startTime = System.currentTimeMillis();
        
        return Mono.fromCallable(() -> {
            // 1. 查询设备ID集合（带缓存）
            List<String> deviceIds = getDeviceIdsByConditions(category, area, building, floor);
            log.info("查询设备ID完成: category={}, area={}, building={}, floor={}, count={}", 
                    category, area, building, floor, deviceIds.size());
            return deviceIds;
        })
        .flatMap(deviceIds -> {
            if (deviceIds.isEmpty()) {
                log.info("未找到符合条件的设备，返回统计结果0");
                return Mono.just(new CameraCountResponse(0L));
            }
            
            // 2. 分批统计摄像头（带性能优化）
            return countCamerasByBatchOptimized(deviceIds, status, token);
        })
        .doOnSuccess(response -> {
            long duration = System.currentTimeMillis() - startTime;
            log.info("摄像头统计完成: count={}, duration={}ms", response.getCount(), duration);
        })
        .doOnError(error -> {
            long duration = System.currentTimeMillis() - startTime;
            log.error("摄像头统计失败: duration={}ms, error={}", duration, error.getMessage(), error);
        });
    }
    
    /**
     * 查询设备ID集合（带缓存）
     */
    @Cacheable(value = "device-ids", 
               key = "#category + '_' + #area + '_' + #building + '_' + #floor",
               unless = "#result.isEmpty()")
    public List<String> getDeviceIdsByConditions(String category, String area, 
                                                String building, String floor) {
        log.debug("从数据库查询设备ID: category={}, area={}, building={}, floor={}", 
                category, area, building, floor);
        
        DeviceQueryRequest request = new DeviceQueryRequest();
        request.setCategory(StringUtils.hasText(category) ? category : "视频监控");
        request.setArea(area);
        request.setBuilding(building);
        request.setFloor(floor);
        
        return deviceService.getDeviceIds(request);
    }
    
    /**
     * 分批统计摄像头（性能优化版本）
     */
    private Mono<CameraCountResponse> countCamerasByBatchOptimized(List<String> deviceIds, 
                                                                 Integer status, String token) {
        // 1. 动态计算最优批次大小
        int optimalBatchSize = calculateOptimalBatchSize(deviceIds.size());
        
        // 2. 分批处理
        List<List<String>> batches = splitIntoBatches(deviceIds, optimalBatchSize);
        
        log.info("开始分批统计: 设备总数={}, 批次数={}, 批次大小={}", 
                deviceIds.size(), batches.size(), optimalBatchSize);
        
        // 3. 并发统计各批次
        List<Mono<Long>> batchMonos = batches.stream()
                .map(batch -> countSingleBatchWithOptimization(batch, status, token))
                .collect(Collectors.toList());

        // 4. 汇总所有批次结果
        return Flux.fromIterable(batchMonos)
                .flatMap(mono -> mono)
                .reduce(0L, Long::sum)
                .map(CameraCountResponse::new)
                .doOnSuccess(response -> 
                    log.info("分批统计完成: 总批次数={}, 总统计数={}", batches.size(), response.getCount()));
    }
    
    /**
     * 单批次统计（带并发控制、重试、超时）
     */
    private Mono<Long> countSingleBatchWithOptimization(List<String> batchDeviceIds,
                                                       Integer status, String token) {
        return Mono.fromCallable(() -> {
            // 获取并发许可
            batchSemaphore.acquire();
            log.debug("获取批次处理许可: batchSize={}, 剩余许可={}", 
                    batchDeviceIds.size(), batchSemaphore.availablePermits());
            return batchDeviceIds;
        })
        .flatMap(deviceIds -> {
            // 执行实际的摄像头统计
            CameraCountRequest request = new CameraCountRequest();
            request.setDevIds(deviceIds);
            request.setStatus(status);
            
            return cameraService.getCameraCount(request, token)
                    .map(response -> response.getCount())
                    .timeout(Duration.ofSeconds(timeoutSeconds))
                    .retryWhen(Retry.backoff(3, Duration.ofMillis(500))
                            .maxBackoff(Duration.ofSeconds(2)))
                    .onErrorReturn(0L); // 出错返回0，不影响其他批次
        })
        .doOnSuccess(count -> 
            log.debug("批次统计成功: batchSize={}, count={}", batchDeviceIds.size(), count))
        .doOnError(error -> 
            log.warn("批次统计失败: batchSize={}, error={}", batchDeviceIds.size(), error.getMessage()))
        .doFinally(signal -> {
            // 释放并发许可
            batchSemaphore.release();
            log.debug("释放批次处理许可: 剩余许可={}", batchSemaphore.availablePermits());
        });
    }
    
    /**
     * 动态计算最优批次大小
     */
    private int calculateOptimalBatchSize(int totalDeviceCount) {
        int adjustedSize = baseBatchSize;
        
        // 基于设备总数调整
        if (totalDeviceCount > 10000) {
            adjustedSize = Math.max(minBatchSize, baseBatchSize / 2);
        } else if (totalDeviceCount < 500) {
            adjustedSize = Math.min(maxBatchSize, baseBatchSize * 2);
        }
        
        // 基于系统负载调整
        double systemLoad = getSystemLoad();
        if (systemLoad > 0.8) {
            adjustedSize = Math.max(minBatchSize, adjustedSize / 2);
        }
        
        log.debug("动态调整批次大小: totalDevices={}, systemLoad={:.2f}, batchSize={}", 
                totalDeviceCount, systemLoad, adjustedSize);
        
        return adjustedSize;
    }
    
    /**
     * 获取系统负载
     */
    private double getSystemLoad() {
        try {
            return ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage();
        } catch (Exception e) {
            return 0.5; // 默认负载
        }
    }
    
    /**
     * 将设备ID列表分批
     */
    private List<List<String>> splitIntoBatches(List<String> deviceIds, int batchSize) {
        List<List<String>> batches = new ArrayList<>();
        for (int i = 0; i < deviceIds.size(); i += batchSize) {
            int end = Math.min(i + batchSize, deviceIds.size());
            batches.add(deviceIds.subList(i, end));
        }
        return batches;
    }
}
